package com.hrpc.channelHandler.handler;

import com.hrpc.ServiceConfig;
import com.hrpc.core.ShutDownHolder;
import com.hrpc.enumeration.RequestType;
import com.hrpc.enumeration.ResponseCode;
import com.hrpc.hRpcBootStarp;
import com.hrpc.protection.RateLimiter;
import com.hrpc.protection.TokenBuketRateLimiter;
import com.hrpc.transport.message.RequestPayload;
import com.hrpc.transport.message.hRpcRequest;
import com.hrpc.transport.message.hRpcResponse;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Map;

public class MethodCallHandler extends SimpleChannelInboundHandler<hRpcRequest> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, hRpcRequest hRpcRequest) throws Exception {
        //1.先封装部分响应
        hRpcResponse hRpcResponse=new hRpcResponse();
        hRpcResponse.setRequestId(hRpcRequest.getRequestId());
        hRpcResponse.setCompressType(hRpcRequest.getCompressType());
        hRpcResponse.setSerializeType(hRpcRequest.getSerializeType());
        //2.获得通道
        Channel channel = channelHandlerContext.channel();
        //3.查看挡板是否打开，打开返回一个CLOSING状态
        if (ShutDownHolder.BAFFLE.get() == true){
            hRpcResponse.setCode(ResponseCode.CLOSING.getCode());
            channel.writeAndFlush(hRpcResponse);
            return;
        }
        ShutDownHolder.REQUEST_COUNT.increment();
        //4.完成限流的工作
        SocketAddress socketAddress = channel.remoteAddress();
        //我们要针对性的限流，就应该一个地址匹配一个限流器
        Map<SocketAddress, RateLimiter> everyIpRateLimiter = hRpcBootStarp.getInstance().getConfiguration().getEveryIpRateLimiter();
        RateLimiter rateLimiter = everyIpRateLimiter.get(socketAddress);
        if (rateLimiter == null){
            rateLimiter=new TokenBuketRateLimiter(5,5);
            everyIpRateLimiter.put(socketAddress,rateLimiter);
        }
        boolean allowRequest = rateLimiter.allowRequest();
        //限流
        if (!allowRequest){
            //需要返回了
            hRpcResponse.setCode(ResponseCode.RATE_LIMIT.getCode());
        } else if (hRpcRequest.getRequestType() == RequestType.HEART_BEAT.getId()){
            //处理心跳
            hRpcResponse.setCode(ResponseCode.SUCCESS_HEART_BEAT.getCode());
            //需要封装响应，并且返回

        }else{
            /**
             * 具体的调用过程，正常调用
             */
            //1.获取负载内容
            RequestPayload requestPayload = hRpcRequest.getRequestPayload();
            try {
                //2.根据负载内容进行方法调用
                Object result = callTargetMethod(requestPayload);
                //3.封装响应
                hRpcResponse.setCode(ResponseCode.SUCCESS.getCode());
                hRpcResponse.setBody(result);
            } catch (Exception e) {
                hRpcResponse.setCode(ResponseCode.FAIL.getCode());
            }
            //5.写出响应
            channel.writeAndFlush(hRpcResponse);
            //6.计数器减一
            ShutDownHolder.REQUEST_COUNT.decrement();
        }
    }

    private Object callTargetMethod(RequestPayload requestPayload) {
        String interfaceName = requestPayload.getInterfaceName();
        String methodName = requestPayload.getMethodName();
        Class<?>[] parametersType = requestPayload.getParametersType();
        Object[] parametersValue = requestPayload.getParametersValue();
        //寻找合适的类完成方法调用
        ServiceConfig<?> serviceConfig = hRpcBootStarp.SERVERS_LIST.get(interfaceName);
        //得到一个具体的实现
        Object referenceImpl = serviceConfig.getReference();
        //通过反射调用
        //1.获取方法对象 2.执行invoke方法
        Object res = null;
        try {
            Class<?> aClass = referenceImpl.getClass();
            Method method = aClass.getMethod(methodName, parametersType);
            res = method.invoke(referenceImpl,parametersValue);
        }catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            throw new RuntimeException("调用"+ interfaceName +"服务时出现了异常");
        }
        return res;
    }
}
